package fr.inserm.um915.bdibd.model;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.sleepycat.je.Cursor;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;

/**
 * 
 * CommandLine
 *
 */
public class CommandLine
	{
	private Model model=new Model();
	private boolean selectedPairIds[]=new boolean[0];
	private boolean printIBD[]=new boolean[]{true,true,true};
	private PrintStream out=System.out;
	private float ibd0Limit=0;

	
	private void printHeader(Transaction txn)
		{
		out.print("#marker");
		out.print('\t');
		out.print("chrom");
		out.print('\t');
		out.print("position");
		out.print('\t');
		out.print("genmap");
		out.print('\t');
		out.print("count(pairs)<"+this.ibd0Limit);
		out.print('\t');
		out.print("mean");
		for(int i=0;i< model.getPairCount();++i)
			{
			if(!selectedPairIds[i]) continue;
			PairOfIndividual pair=model.getPairOfIndividualByPairId(txn, i);
			for(int j=0;j<3;++j)
				{
				if(!printIBD[j]) continue;
				out.print('\t');
				out.print(pair.getLabel()+"|IBD"+j);
				}
			}
		out.println();
		}
	private void printRow(Transaction txn,Marker marker,LinkageRow statuses)
		{
		out.print(marker.getName());
		out.print('\t');
		out.print(marker.getPosition().getChromosome());
		out.print('\t');
		out.print(marker.getPosition().getPosition());
		out.print('\t');
		out.print(marker.getGenMapAsString());
		
		int count_pairs=0;
		double total=0;
		for(int i=0;i< statuses.size();++i)
			{
			if(!selectedPairIds[i]) continue;
			if(statuses.get(i).getIbd0() < this.ibd0Limit) continue;
			count_pairs++;
			total+=statuses.get(i).getIbd0();
			}
		out.print('\t');
		out.print(count_pairs);
		out.print('\t');
		out.print(count_pairs==0?"nan":String.valueOf(total/count_pairs));
		
		for(int i=0;i< statuses.size();++i)
			{
			if(!selectedPairIds[i]) continue;
			
			for(int j=0;j<3;++j)
				{
				if(!printIBD[j]) continue;
				out.print('\t');
				out.print(statuses.get(j));
				}
			}
		out.println();
		}
	
	
	private void scanMarker(Transaction txn,String rsName)
		{
		LinkageRow statuses=model.getIBDByMarker(txn, rsName);
		if(statuses==null)
			{
			System.err.println("Marker unknown:"+rsName);
			return;
			}
		Marker m=model.getMarkerByName(txn, rsName);
		if(m==null)
			{
			throw new RuntimeException("where is marker "+rsName+" ?");
			}
		printRow(txn,m,statuses);
		}
	
	private void scanMarkers(Transaction txn,BufferedReader in) throws IOException
		{
		String line;
		while((line=in.readLine())!=null)
			{
			if(line.isEmpty()) continue;
			if(line.startsWith("#")) continue;
			scanMarker(txn,line);
			}
		}
	
	private void scanRegion(Transaction txn,ChromStartEnd region) throws IOException
		{
		Cursor c=null;
		try {
			c=model.getPosToMarkerDatabase().openCursor(txn, null);
			boolean first=true;
			DatabaseEntry key=new DatabaseEntry();
			DatabaseEntry data=new DatabaseEntry();
			for(;;)
				{
				OperationStatus ret;
				if(first)
					{
					ret=c.getSearchKeyRange(key, data, null);
					}
				else
					{
					ret=c.getNext(key, data, null);
					}
				first=false;
				if(ret!=OperationStatus.SUCCESS) break;
				ChromPosition pos=ChromPosition.BINDING.entryToObject(key);
				if(pos.getChromosomeId()< region.getChromosomeId()) continue;
				if(pos.getChromosomeId()> region.getChromosomeId()) break;
				if(pos.getPosition()< region.getStart()) continue;
				if(pos.getPosition()> region.getEnd()) continue;
				Marker m=Marker.BINDING.entryToObject(data);
				LinkageRow statuses=model.getIBDByMarker(txn, m.getName());
				if(statuses==null)
					{
					System.err.println("??No data for "+m);
					return;
					}
				printRow(txn, m, statuses);
				}
			} 
		finally
			{
			if(c!=null) c.close();
			}
		}
	
	public void open(File dbHome) throws IOException
		{
		close();
		this.model.open(dbHome, true);
		this.printIBD=new boolean[this.model.getPairCount()];
		Arrays.fill(this.printIBD, true);
		}
	
	public void close()
		{
		this.model.close();
		}
	
	private void usage(String args[])
		{
		System.out.println("  -d (directory) bdb-home");
		System.out.println("  -r (region) chrom:start-end");
		System.out.println("  -g (file) save graphics as");
		System.out.println("  -L (float)  limit");
		System.out.println("  -ibd0 don't show ibd0");
		System.out.println("  -ibd1 don't show ibd1");
		System.out.println("  -ibd2 don't show ibd1");
		System.out.println("  --restrict-family (fam)");
		System.out.println("  --restrict-individual (fam) (indi)");
		System.out.println();
		System.out.println("markers in (stdin|files)");
		}
	/**
	 * @param args
	 */
	public static void main(String[] args)
		{
		File saveAsGraphics=null;
		ChromStartEnd region=null;
		Transaction txn=null;
		File dbHome=null;
		Set<String> restrictFamilies=new HashSet<String>();
		List<String> restrictIndividuals=new ArrayList<String>();
		
		int optind=0;
		final CommandLine app=new CommandLine();
		while(optind< args.length)
			{
			if(args[optind].equals("-h"))
				{
				app.usage(args);
				return;
				}
			else if(args[optind].equals("-g"))
				{
				saveAsGraphics=new File(args[++optind]);
				}
			else if(args[optind].equals("-ibd0"))
				{
				app.printIBD[0]=!app.printIBD[0];
				}
			else if(args[optind].equals("-ibd1"))
				{
				app.printIBD[1]=!app.printIBD[1];
				}
			else if(args[optind].equals("-ibd2"))
				{
				app.printIBD[2]=!app.printIBD[2];
				}
			else if(args[optind].equals("-r"))
				{
				region=ChromStartEnd.parse(args[++optind]);
				if(region==null)
					{				
					System.err.println("Bad region:"+args[optind]);
					app.usage(args);
					return;
					}
				}
			else if(args[optind].equals("-d") &&  optind+1< args.length)
				{
				dbHome=new File(args[++optind]);
				}
			else if(args[optind].equals("-L") &&  optind+1< args.length)
				{
				app.ibd0Limit=Float.parseFloat(args[++optind]);
				}
			else if(args[optind].equals("--restrict-family") &&  optind+1< args.length)
				{
				restrictFamilies.add(args[++optind]);
				}
			else if(args[optind].equals("--restrict-individual") &&  optind+2< args.length)
				{
				restrictIndividuals.add(args[++optind]);
				restrictIndividuals.add(args[++optind]);
				}
			else if(args[optind].equals("--"))
				{
				++optind;
				break;
				}
			else if(args[optind].startsWith("-"))
				{
				app.usage(args);
				System.err.println("Unknown option "+args[optind]);
				}
			else
				{
				break;
				}
			++optind;
			}
		
		if(dbHome==null)
			{
			System.err.println("dbHome undefined");
			return;
			}
		
		Runtime.getRuntime().addShutdownHook(new Thread()
			{
			@Override
			public void run() {
				app.close();
				}
			});
		try {
			app.open(dbHome);
			
			Set<Individual> selIndividualsFam=new HashSet<Individual>();
			Set<Individual> selIndividualsIndis=new HashSet<Individual>();
			
			if(!restrictFamilies.isEmpty())
				{
				for(String fName:restrictFamilies)
					{
					List<Individual> indis=app.model.getIndividualsByFamily(txn,fName);
					if(indis.isEmpty())
						{
						System.err.println("Error: no individuals for family:"+fName);
						return;
						}
					selIndividualsFam.addAll(indis);
					}
				}
			if(!restrictIndividuals.isEmpty())
				{
				for(int i=0;i+1< restrictIndividuals.size();i+=2)
					{
					Individual indi=app.model.getIndividualbyName(txn,
						restrictIndividuals.get(i),
						restrictIndividuals.get(i+1)
						);
					if(indi==null)
						{
						System.err.println("Error: no individual "+
								restrictIndividuals.get(i)+
								" / "+
								restrictIndividuals.get(i+1)
								);
						return;
						}
					
					selIndividualsIndis.add(indi);
					}
				}
			
			Set<Individual> selIndividuals=selIndividualsIndis;
			if(!selIndividualsFam.isEmpty())
				{
				selIndividuals.retainAll(selIndividualsFam);
				}
			
			if(selIndividuals.isEmpty())
				{
				Arrays.fill(app.selectedPairIds,false);
				List<Individual> v=new ArrayList<Individual>(selIndividuals);
				for(int i=0;i< v.size();++i)
					{
					for(int j=0;j< v.size();++j)
						{
						int pair_id=app.model.findPairIdByIndividuals(txn, v.get(i),v.get(j));
						if(pair_id==-1) continue;
						app.selectedPairIds[pair_id]=true;
						}	
					}	
				}
			if(saveAsGraphics!=null)
				{
				Set<Integer> pairids=new HashSet<Integer>();
				for(int i=0;i< app.selectedPairIds.length;++i)
					{
					if(app.selectedPairIds[i]) pairids.add(i);
					}
				ChromPosition regionBegin=null;
				ChromPosition regionEnd=null;
				
				if(region!=null)
					{
					regionBegin=new ChromPosition(region.getChromosomeId(),region.getStart());
					regionEnd=new ChromPosition(region.getChromosomeId(),region.getEnd());
					}
				
				ImageBuilder image=new ImageBuilder();
				image.build(new DataExtractorFactoryIBDCount(WhichIBD.USE_IBD_0, true), regionBegin, regionEnd, pairids);
				image.saveAs(saveAsGraphics);
				}
			else if(region!=null)
				{
				if(optind!=args.length)
					{
					System.err.println("Illegal number of arguments");
					return;
					}
				app.printHeader(txn);
				app.scanRegion(txn, region);
				}
			else if(optind==args.length)
				{
				app.printHeader(txn);
				app.scanMarkers(txn, new BufferedReader(new InputStreamReader(System.in)));
				}
			else
				{
				app.printHeader(txn);
				while(optind<args.length)
					{
					String fname=args[optind++];
					BufferedReader in=IOUtils.open(new File(fname));
					app.scanMarkers(txn, in);
					in.close();
					}
				}
			}
		catch (Exception e) {
			e.printStackTrace();
			}
		finally
			{
			app.close();
			}
		}
}
